<html>
  <head></head>
  <body>
    <div>
      <p>defineProperty</p>
    </div>
    <script>
      const userInfo = {
        name: 'burc',
        age: 23,
        hobbys: ['react', 'vue', 'js'],
        school: {
          name: '一中',
          address: '商河县',
        },
      }

      function observer(data) {
        if (typeof data !== 'object' || !data || Array.isArray(data)) {
          return
        }
        for (const key in data) {
          if (Object.hasOwnProperty.call(data, key)) {
            defineProperty(data, key)
          }
        }
      }

      function defineProperty(data, key) {
        let value = data[key]
        observer(value)
        Object.defineProperty(data, key, {
          // 如已设置 set 或 get, 就不能设置 writable 和 value 中的任何一个了，否则报错：Cannot both specify accessors and a value or writable attribute
          // 当 writable 属性设置为 false 时，该属性被称为“不可写的”。它不能被重新赋值
          // value: 99,
          // writable: false,

          // enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举
          // ɪ'nju:mərəbəl 可枚举的
          enumerable: true,

          // configurable 特性表示对象的属性是否可以被删除，以及除 value 和 writable 特性外的其他特性是否可以被修改。
          // kənˈfɪgərəbl 可配置的
          configurable: true,

          get: function () {
            console.log('触发get:', key, value, this)
            return value
          },

          set: function (newValue) {
            if (newValue === value) {
              return
            }
            observer(newValue)
            console.log('触发set:', JSON.stringify(newValue))
            value = newValue
          },
        })
      }

      observer(userInfo)

      // userInfo.name = {
      //   firstName: '李',
      //   lastName: '百成',
      // }

      // userInfo.name.firstName = '张'

      userInfo.hobbys[1] = 999
    </script>
  </body>
</html>
